/*
 * ESPRESSIF MIT License
 *
 * Copyright (c) 2015 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
 *
 * Permission is hereby granted for use on ESPRESSIF SYSTEMS ESP8266 only, in which case,
 * it is free of charge, to any person obtaining a copy of this software and associated
 * documentation files (the "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the Software is furnished
 * to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or
 * substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 */

/*****************************************************
** Name         : fota_crc32.c
** Author       : tianzx
** Version      : 1.0
** Date         : 2016-9
** Description  : CRC32 Checking
******************************************************/
#include "esp_common.h"

#include "lwip/err.h"
#include "lwip/ip_addr.h"
#include "lwip/mem.h"
#include <stdlib.h>

#define BUFSIZE     512
#define CRC_BLOCK_SIZE 512
uint16 start_sec;
static unsigned int *crc_table;

#ifdef MEMLEAK_DEBUG
static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__;
#endif

static int init_crc_table(void);
static unsigned int crc32(unsigned int crc, unsigned char *buffer, unsigned int size);

static int ICACHE_FLASH_ATTR
init_crc_table(void)
{
    unsigned int c;
    unsigned int i, j;

    crc_table = (unsigned int *)zalloc(256 * 4);

    if (crc_table == NULL) {
        printf("malloc crc table failed\n");
        return -1;
    }

    for (i = 0; i < 256; i++) {
        c = (unsigned int)i;

        for (j = 0; j < 8; j++) {
            if (c & 1) {
                c = 0xedb88320L ^ (c >> 1);
            } else {
                c = c >> 1;
            }
        }

        crc_table[i] = c;
    }

    return 0;
}

static unsigned int ICACHE_FLASH_ATTR
crc32(unsigned int crc, unsigned char *buffer, unsigned int size)
{
    unsigned int i;

    for (i = 0; i < size; i++) {
        crc = crc_table[(crc ^ buffer[i]) & 0xff] ^ (crc >> 8);
    }

    return crc ;
}


static int ICACHE_FLASH_ATTR
calc_img_crc(unsigned int sumlength, unsigned int *img_crc)
{
    int fd;
    int ret;
    int i = 0;
    uint8 error = 0;
    unsigned char *buf = (char *)zalloc(BUFSIZE);

    if (buf == NULL) {
        printf("malloc crc buf failed\n");
        free(crc_table);
        return -1;
    }

    unsigned int crc = 0xffffffff;

    uint16 sec_block = sumlength / CRC_BLOCK_SIZE ;
    uint32 sec_last = sumlength % CRC_BLOCK_SIZE;

    for (i = 0; i < sec_block; i++) {
        if (0 != (error = spi_flash_read(start_sec * SPI_FLASH_SEC_SIZE + i * CRC_BLOCK_SIZE , (uint32 *)buf, BUFSIZE))) {
            free(crc_table);
            free(buf);
            printf("spi_flash_read error %d\n", error);
            return -1;
        }

        crc = crc32(crc, buf, BUFSIZE);
    }

    if (sec_last > 0) {
        if (0 != (error = spi_flash_read(start_sec * SPI_FLASH_SEC_SIZE + i * CRC_BLOCK_SIZE, (uint32 *)buf, sec_last))) {
            free(crc_table);
            free(buf);
            printf("spi_flash_read error %d\n", error);
            return -1;
        }

        crc = crc32(crc, buf, sec_last);
    }

    *img_crc = crc;
    free(crc_table);
    free(buf);
    return 0;
}

bool ICACHE_FLASH_ATTR
upgrade_crc_check(uint16 fw_bin_sec , unsigned int sumlength)
{
    printf("fw_bin_sec %d sumlength %d\n", fw_bin_sec, sumlength);
    int ret;
    unsigned int img_crc;
    unsigned int flash_crc = 0xFF;
    start_sec = fw_bin_sec;

    if (0 != init_crc_table()) {
        return false;
    }

    ret = calc_img_crc(sumlength - 4, &img_crc);

    if (ret < 0) {
        return false;
    }

    img_crc = abs(img_crc);
    printf("img_crc = %u\n", img_crc);
    spi_flash_read(start_sec * SPI_FLASH_SEC_SIZE + sumlength - 4, &flash_crc, 4);
    printf("flash_crc = %u\n", flash_crc);

    if (img_crc == flash_crc) {
        return true;
    } else {
        return false;
    }
}
